//fl2440_pwm.c
#include <linux/module.h> 
#include <linux/fs.h>//添加和注销,设备驱动程序提供入口点 
#include <asm/io.h>//对设备进行管理的函数入口 
#include <linux/init.h> 
#include <linux/kernel.h>//内存操作 
#include <linux/cdev.h> 
#include <linux/moduleparam.h>//定义模块可以传递参数 
#include <asm/arch/regs-gpio.h>//GPIO口相关的宏定义 
#include <asm/hardware.h>//GPIO口操作相关的函数定义 
#include <asm/arch/regs-timer.h>//S3C2410_TCFG等的定义
 
#define DEVICE_NAME "fl2440_pwm" 
#define DEVICE_MAJOR 230      
#define PWM_IOCTL_SET_FREQ 1    //定义宏变量,用于后面的 ioctl 中的控制命令  
#define PWM_IOCTL_STOP 0

int devno;

struct pwm_dev {  
  struct cdev cdev; 
}pwm1_dev;
  
//定义信号量 lock用于互斥，因此，该驱动程序只能同时有一个进程使用  
static struct semaphore lock;  
  
/* freq: pclk/50/16/65536 ~ pclk/50/16 
* if pclk = 50MHz, freq is 1Hz to 62500Hz 
* human ear(人儿能辨别的) : 20Hz~ 20000Hz 
*/  
/*配置各个寄存器，设置PWM的频率*/  
static void PWM_Set_Freq( unsigned long freq )   
{  
 unsigned long tcon;  
 unsigned long tcnt;   
 unsigned long tcfg1;  
 unsigned long tcfg0;  
 struct clk *clk_p;  
 unsigned long pclk;  
  
 //设置GPB0 为TOUT0,pwm 输出  
 s3c2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPB0_TOUT0);  
  
 /*读取定时器控制寄存器的数值*/  
 tcon = __raw_readl(S3C2410_TCON);   
 /*读取定时器配置寄存器1的值*/  
 tcfg1 = __raw_readl(S3C2410_TCFG1);   
 /*读取定时器配置寄存器0的值*/  
 tcfg0 = __raw_readl(S3C2410_TCFG0);   
  
 /*设置预分频prescaler = 50*/  
 //S3C2410_TCFG_PRESCALER0_MASK定时器0 和1 的预分频值的掩码，TCFG[0~7]  
 tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;//低八位清零  
 tcfg0 |= (50 - 1); //设置预分频为50  
  
 //S3C2410_TCFG1_MUX0_MASK 定时器0 分割值的掩码TCFG1[0~3]  
 tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK;   
 //定时器0 进行16 分割  
 tcfg1 |= S3C2410_TCFG1_MUX0_DIV16;   
   
 /*将设置的参数值写入相应的寄存器中*/  
 //把tcfg1 的值写到分割寄存器S3C2410_TCFG1 中  
 __raw_writel(tcfg1, S3C2410_TCFG1);   
 //把tcfg0 的值写到预分频寄存器S3C2410_TCFG0 中  
 __raw_writel(tcfg0, S3C2410_TCFG0);   

  /*开启对应时钟源，并获取pclk*/  
 clk_p = clk_get(NULL, "pclk");   
 //获得pclk的时钟频率  
 pclk = clk_get_rate(clk_p);  
   
 /*得到定时器的输入时钟，进而设置PWM的调制频率和占空比*/  
 tcnt = (pclk/50/16)/freq;   
 //PWM 脉宽调制的频率等于定时器的输入时钟  
 __raw_writel(freq, S3C2410_TCNTB(0));   
  //占空比是50%  
 __raw_writel(freq/2, S3C2410_TCMPB(0));  
  
 /*失能死区，开启自动重载, 关闭变相, 更新TCNTB0&TCMPB0, 启动timer0*/  
 tcon &= ~0x1f;  
 tcon |= 0xb;   
 //把tcon 写到计数器控制寄存器S3C2410_TCON 中  
 __raw_writel(tcon, S3C2410_TCON);   
  
 //clear manual update bit  
 tcon &= ~0x2;   
 __raw_writel(tcon, S3C2410_TCON);  
}  
  
static void pwm_stop(void)  
{  
 //设置GPB0 为输出  
 s3c2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPB0_OUTP);   
 //设置GPB0 为低电平，使蜂鸣器停止  
 s3c2410_gpio_setpin(S3C2410_GPB0, 0);   
}  
  
static int pwm_open(struct inode *inode, struct file *file)  
{  
 if (!down_trylock(&lock)) //是否获得信号量，是down_trylock(&lock）=0，否则非0  
  return 0;  
 else  
  return -EBUSY; //返回错误信息：请求的资源不可用  
}  

static int pwm_close(struct inode *inode, struct file *file)  
{  
  pwm_stop();  
  //释放信号量lock  
  up(&lock);   
  return 0;  
}  
  
/*cmd 是1，表示设置频率；cmd 是0 ，表示停止pwm*/  
static int pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)  
{  
 switch (cmd)   
 {  
  case PWM_IOCTL_SET_FREQ: //if cmd=1 即进入case PWM_IOCTL_SET_FREQ  
       if (arg == 0) //如果设置的频率参数是0  
            return -EINVAL; //返回错误信息，表示向参数传递了无效的参数  
       PWM_Set_Freq(arg); //否则设置频率  
  break;
  case PWM_IOCTL_STOP: // if cmd=0 即进入case PWM_IOCTL_STOP  
        pwm_stop(); //停止蜂鸣器  
  break;  
 }  
 return 0; //成功返回  
}  
  
/*初始化设备的文件操作的结构体*/  
static struct file_operations pwm_fops = {  
 .owner = THIS_MODULE,  
 .open = pwm_open,  
 .release = pwm_close,  
 .ioctl = pwm_ioctl,  
};  

int major;  
static int __init pwm_init(void)  
{
  int ret, err; 
   
  init_MUTEX(&lock);   
  
  ret = register_chrdev(DEVICE_MAJOR, DEVICE_NAME, &pwm_fops);
  if (ret < 0) {
    printk(DEVICE_NAME " can't register major number\n");
    return ret;
  }

  printk (DEVICE_NAME " initialized!\n");

  devfs_mk_cdev(MKDEV(DEVICE_MAJOR, 0), S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,DEVICE_NAME);  
  ret = alloc_chrdev_region(&devno,0,1,DEVICE_NAME); //在内核中注册设备  
  if (ret < 0){   
    printk(DEVICE_NAME "register failure!\n");     
    return ret;  
  }else{   
    printk(DEVICE_NAME " register success!\n");    
    cdev_init(&pwm1_dev.cdev,&pwm_fops);   
    pwm1_dev.cdev.owner = THIS_MODULE;   
    pwm1_dev.cdev.ops = &pwm_fops;   
    err = cdev_add(&pwm1_dev.cdev,devno,1);//创建设备节点   
    if (err){    
      printk(DEVICE_NAME " Add failure!\n");   
    }else{    
      printk(DEVICE_NAME " Creat success!\n");   
    }
  }  
  return 0; 
} 

static void __exit pwm_exit(void)  
{  
  unregister_chrdev_region(devno,1);//注销设备  
  cdev_del(&pwm1_dev.cdev);//删除设备节点  
  printk(DEVICE_NAME "unregister success!\n"); 
}
module_init(pwm_init);  
module_exit(pwm_exit);  
  
MODULE_LICENSE("GPL");//宏声明此模块的许可证  
MODULE_AUTHOR("wangyijieonline@126.com"); 
MODULE_DESCRIPTION("fl2440 pwm driver");


